package com.zklh.book.other.order.service;

import com.zklh.book.other.order.model.Order;
import com.zklh.book.other.order.model.Product;

import java.util.*;

/**
 * 订单处理器
 *
 * @author 坐看落花
 * @version 1.0
 * @date 2021/5/9 8:44
 */
public class OrderHandler {



    private Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {

        /**
         * 在这里稍微说一下编程什么是优雅, 编程前期可以靠感觉去编, 但是后面随着代码量的提升, 一定是可以区分出来什么是好的代码, 什么是差的代码的;
         * 优雅本身就是在说代码组织方式的好坏, 好的代码复用率高, 可读性高, 基本不用注释也能读懂, 而且要符合现实逻辑, 什么现实逻辑呢, 就是看到这个
         * 函数的命名就知道它做了什么, 看到一个对象(或者说一个类), 就应该知道它的作用是什么, 它应该有哪些功能, 哪些功能放到它身上是不合适的, 哪些属性同理
         *
         * 比如说这里让做一个开门的功能,  此时有两个模型, 人和门,  那么人有身高,体重,这些属性,  门有宽,高, 这些属性, 那么问题来了, 有个开门的功能, 也就是函数, 这个函数应该放在哪里呢,
         * 这个是人的功能对吧, 应该放在人上面, 调用的话就是 man.open(), 但是如果是自动门呢, 那可能就是door.open()了, 这是想说明什么呢, 就是模型或者说对象的设计一定是符合现实的, 这么设计的目的
         * 本质上还是便于理解, 现实中人们怎么去认知它的, 计算机中就模仿这种认知去定义, 这样才会更合理,
         *
         * 你现在的问题更多的是没有做过面向对象的设计, 面向对象编程核心其实在上面已经有了体现了, 但是具体怎么去设计, 需要自己仔细思考, 反复权衡, 什么样的设计才是合理的, 才是符合
         * 逻辑的, 这个我认为是区别好坏程序员的一个标准之一, 而且模型设计是我认为占了技术 一半 的东西, 关于设计也是有书可以读的, 比如设计模式是基于面向对象设计做的比较优雅的常用的模型, 更多
         * 的还是要自己多参与一些设计, 反复权衡, 好的设计一定是能经得起时间的考量的,  还有一点, 没有最好的设计, 设计的模型也是可能会发生变化的, 现在是这一套, 半年后可能会相应的做调整, 设计是
         * 动态的, 是有可能会变化的, 模型设计要能容忍这些变化, 或者说变化的时候, 可以改, 但尽量要少
         */

        /**
         * 这里补充一点, 0514早上想到的想要补充的, 关于设计, 我理解的话分为两种 一种是模型设计,一种是代码上的模型设计(没有数据的模型),
         * 前者也就是数据库设计或者说业务模型设计,
         * 后者是代码结构设计
         * 这两者 有相通的地方, 但前者更注重更多的依赖现实逻辑, 而且现实中经常会有体现, 后者虽然也依赖符合现实逻辑, 但抽象的模型往往是现实中不存在的, 是自己想象的, 不过设计模式提供了一套模板 辅助你去抽象现实中不存在的模型
         *
         * 前者的神书是ddd 也就是领域驱动模型
         * 后者的神书是设计模式,
         * 你也可以自己找找看相应的书籍, 前面只要知道面向对象设计, 并且会做一些简单的设计就行了,
         */

        OrderHandler orderHandler = new OrderHandler();
        // 创建盛放订单和商品的容器
        List<Product> products = new ArrayList<>();
        List<Order> orders = new ArrayList<>();

        // 读取数据, 并将数据放入容器中
        orderHandler.readData(products, orders);

        // 将读取到的数据作为calculate的参数, 进行运算得到结果
        int profit = 0;
        try {
            profit = orderHandler.calculateProfit(products, orders);
        } catch (Exception e) { // 这里捕获异常, 处理方式是告知用户计算错误, 并输入错误信息, 尝试输入订单的商品名称在商品中不存在, 或者订单amount 超过商品剩余库存. 会出现错误情况
            System.out.println("订单处理错误, 错误信息如下: ");
            System.out.println(e.getMessage());
        }

        // 输入结果
        System.out.println(profit);
    }

    /**
     * 用户读取控制台输入的数据, 这里读取数据全放一起了, 然后orders和products为全局变量, 并不是传入的
     */
    public void readData(List<Product> products,  List<Order> orders){
        // 获取第一行, 空格分开, 第一个为商品数, 第二个是订单数量
        String str = scanner.nextLine();
        String[] cnts = str.split(" ");
        int productCnt = Integer.parseInt(cnts[0]);
        int orderCnt = Integer.parseInt(cnts[1]);

        // 读取商品信息
        for (int i = 0; i < productCnt; i++) {
            String productStr = scanner.nextLine();
            String[] productAttrs = productStr.split(" ");
            String name = productAttrs[0];
            int profit = Integer.parseInt(productAttrs[1]);
            int store = Integer.parseInt(productAttrs[2]);
            Product product = new Product(name,profit,store);
            products.add(product);
        }

        // 读取订单信息
        for (int i = 0; i < orderCnt; i++) {
            String orderStr = scanner.nextLine();
            String[] productAttrs = orderStr.split(" ");
            String productName = productAttrs[0];
            int amount = Integer.parseInt(productAttrs[1]);
            Order order = new Order(productName, amount);
            orders.add(order);
        }
    }


    public int calculateProfit(List<Product> products, List<Order> orders){
        // 商品map表, 用于通过商品名称定位到商品的信息
        Map<String, Product> productMap = new HashMap<>();
        for (Product product : products) {
            productMap.put(product.getName(), product);
        }

        // 遍历订单, 然后通过productMap 获取商品以及收益信息, 计算总收益
        int sumProfit = 0;
        for (Order order : orders) {
            Product product = productMap.get(order.getProductName());
            if (product == null) {
                // 这里当商品为空抛出异常, 这里不允许, 因为如果商品找不到, 没有办法计算价格, 计算的结果也不可能准确, 所以直接抛异常终止程序的运行, 上层可以拦截该异常, 然后做相应的业务处理, 比如说把异常打印出来
                // 异常的作用本质上是报告错误信息以及错误发生的位置, 异常是很重要的
                throw new RuntimeException("数据错误, 指定商品不存在!");
            }
            if (product.getStore() < order.getAmount()) {
                throw new RuntimeException("数据错误! "+product.getName()+"库存不足!");
            }
            sumProfit += order.getAmount() * product.getProfit();
            product.setStore(product.getStore()-order.getAmount());
        }
        return sumProfit;
    }



}
